﻿
    using System;
	using System.Collections.Generic;
	using System.Threading.Tasks;


namespace DailyFx.UI 
{
    /// <summary>
    ///		Allows custom code to execute after the return of a action.
    /// </summary>
    public interface ICommand
	{
        /// <summary>
        /// Executes the result using the specified context.
        /// </summary>
        /// <param name="context">The context.</param>
        void Call ( WeakAction context );

        /// <summary>
        /// Occurs when execution has completed.
        /// </summary>
        event EventHandler<CommandEventArgs> Completed;
    }

#if !SILVERLIGHT || SL5 || WP8
    /// <summary>
    ///		Allows custom code to execute after the return of a action.
    /// </summary>
    public interface ICommand<out TResult> : ICommand
    {
        /// <summary>
        /// Gets the result of the asynchronous operation.
        /// </summary>
        TResult Result { get; }
    }
#endif


	 /// <summary>
    ///		Common <see cref = "ICommand" />, that enables sequential execution of multiple tasks .
    /// </summary>
    public class MarshalCommand: ICommand
	{
		/// <summary>
		///		Contains all task to do ( sequentially ) .
		/// </summary>
        readonly IEnumerator<ICommand> _todoCmds;

		/// <summary>
		///		the action context for this task(s) .
		/// </summary>
        WeakAction _context;

        /// <summary>
        ///   C'tor
        /// </summary>
		public MarshalCommand (IEnumerator<ICommand> todo) 
		{
            this._todoCmds = todo ;
        }


		/// <summary>
		///		Runs the task(s) using the specified context.
		/// </summary>
		public void Call ( WeakAction context )
		{
			this._context = context ;
			CallImpl( null, new CommandEventArgs() );
		}

      

        void CallImpl ( object sender, CommandEventArgs args )
		{
            var prevCall = sender as ICommand;
            if (prevCall != null)
			{
                prevCall.Completed -= CallImpl;
            }

            if( args.Error != null || args.Cancelled )
			{
				// something goes wrong ...
                OnCompleted ( args.Error, args.Cancelled );
                return;
            }

            var ok = false;
            try 
			{
                ok = _todoCmds.MoveNext();
            }
            catch( Exception exception ) 
			{
                this.OnCompleted ( exception, false );
                return;
            }

            if( ok ) 
			{
                try 
				{
                    var nextCall = _todoCmds.Current;
                    IoC.Satisfy ( nextCall );

                    nextCall.Completed += CallImpl;
                    nextCall.Call ( this._context );
                }
                catch ( Exception exception )
				{
					this.OnCompleted ( exception, false );
                    return;
                }
            }
            else 
			{
                this.OnCompleted ( null, false );
            }
        }

		/// <summary>
		///   Occurs when execution has completed.
		/// </summary>
		public event EventHandler<CommandEventArgs> Completed = delegate { };

     
		/// <summary>
		/// 
		/// </summary>
        void OnCompleted ( Exception error, bool cancelled )
		{
            this._todoCmds.Dispose() ;
            this.Completed ( this, new CommandEventArgs { Error = error, Cancelled = cancelled });
        }
    }


	/// <summary>
	///		<see cref="System.Threading.Tasks.Task"/> Wrapper for a simple command.
	/// </summary>
	public class Command : ICommand
	{
		/// <summary>
		///		the actual task to run .
		/// </summary>
		readonly Task _task ;


		/// <summary>
		/// 
		/// </summary>
		public Command ( Task task )
		{
			this. _task = task;
		}

		/// <summary>
		///		Runs the ui task using the specified context.
		/// </summary>
		public void Call ( WeakAction context )
		{
			if ( this._task.IsCompleted )
			{
				this.OnCompleted( this._task );
			}
			else
			{
				_task.ContinueWith ( this.OnCompleted,
									System.Threading.SynchronizationContext.Current != null 
									? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Current
					);
			}
		}

		/// <summary>
		///		Occurs when execution has completed.
		/// </summary>
		public event EventHandler<CommandEventArgs> Completed = delegate { };

		/// <summary>
		///		Called when the asynchronous task has completed.
		/// </summary>
		protected virtual void OnCompleted ( Task task )
		{
			this.Completed ( this, new CommandEventArgs 
									{  Error = task.Exception, Cancelled = task.IsCanceled, }
			);
		}

	}


	/// <summary>
	///		 <see cref="System.Threading.Tasks.Task&lt;TResult&gt;"/> Wrapper for a simple command .
	/// </summary>
	public class Command<TResult> : Command, ICommand<TResult>
	{
		/// <summary>
		/// 
		/// </summary>
		public Command ( Task<TResult> task )
			: base ( task )
		{
		}

		/// <summary>
		///		Gets the result of the asynchronous operation.
		/// </summary>
		public TResult Result 
		{ 
			get; 
			private set; 
		}

		/// <summary>
		///		Called when the asynchronous task has completed.
		/// </summary>
		protected override void OnCompleted ( Task task )
		{
			if ( !task.IsFaulted && !task.IsCanceled )
				this.Result = ( (Task<TResult>)task ).Result;

			base.OnCompleted( task );
		}

	}


	/// <summary>
	///		
	/// </summary>
	public static class CommandExtension
	{
		/// <summary>
		///		Runs an <see cref="DailyFx.UI.ICommand"/> asynchronous.
		/// </summary>
		public static Task CallEx ( this ICommand cmd, WeakAction context = null )
		{
			return CallAsync<object>(cmd, context);
		}

		/// <summary>
		///		Runs an <see cref="DailyFx.UI.ICommand&lt;TResult&gt;"/> asynchronous.
		/// </summary>
		public static Task<TResult> CallEx<TResult> ( this ICommand<TResult> cmd, WeakAction context = null )
		{
			return CallAsync<TResult>(cmd, context);
		}

		static Task<TResult> CallAsync<TResult> ( ICommand cmd, WeakAction context)
		{
			//
			var source = new TaskCompletionSource<TResult>();

			EventHandler<CommandEventArgs> completed = null;
			completed = (s, e) =>
			{
				cmd.Completed -= completed;

				if ( e.Error != null)
				{
					source.SetException ( e.Error );
				}
				else if ( e.Cancelled )
				{
					source.SetCanceled ( );
				}
				else
				{
					var c = cmd as ICommand<TResult>;
					source.SetResult ( c != null ? c.Result : default(TResult) );
				}
			};

			try
			{
				IoC.Satisfy( cmd );
				cmd.Completed += completed;
				cmd.Call ( context ?? new WeakAction() );
			}
			catch ( Exception exception )
			{
				cmd.Completed -= completed;
				source.SetException( exception );
			}

			return source.Task ;
		}

	}

	/// <summary>
	///		The event args for the Completed event of an <see cref="ICommand"/>.
	/// </summary>
	public class CommandEventArgs : EventArgs
	{
		/// <summary>
		/// Gets or sets the error if one occurred.
		/// </summary>
		/// <value>The error.</value>
		public Exception Error;

		/// <summary>
		/// Gets or sets a value indicating whether the result was cancelled.
		/// </summary>
		/// <value><c>true</c> if cancelled; otherwise, <c>false</c>.</value>
		public bool Cancelled;
	}


}
